/*
 * 描          述:  <描述>
 * 修  改   人:  Administrator
 * 修改时间:  2016年10月10日
 * <修改描述:>
 */
package com.tx.component.task.interceptor;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;

import org.apache.commons.collections4.map.HashedMap;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.ProxyProcessorSupport;
import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry;
import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.SmartClassLoader;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodCallback;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSONObject;
import com.tx.component.dailytask.executor.impl.SimpleDailyTaskExecutor;
import com.tx.component.task.annotations.TaskClass;
import com.tx.component.task.annotations.TaskMethod;
import com.tx.component.task.exeception.TaskContextInitException;
import com.tx.component.task.model.TaskDef;
import com.tx.component.task.service.TaskContextRegistry;
import com.tx.core.exceptions.util.AssertUtils;
import com.tx.core.util.MessageUtils;

/**
 * 动态表业务层缓存创建器<br/>
 * <功能详细描述>
 * 
 * @author  Administrator
 * @version  [版本号, 2016年10月10日]
 * @see  [相关类/方法]
 * @since  [产品/模块版本]
 */
public class TaskInterceptorBeanPostProcessor extends ProxyProcessorSupport
        implements BeanPostProcessor, InitializingBean {
    
    /** 注释内容 */
    private static final long serialVersionUID = 5717953769982845279L;
    
    /** Default is global AdvisorAdapterRegistry. */
    private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry
            .getInstance();
    
    /** 签名 */
    private String signature;
    
    /** 模块 */
    private String module;
    
    /** 任务容器注册表 */
    private TaskContextRegistry registry;
    
    /** 任务执行拦截器工厂 */
    private TaskExecuteInterceptorFactory interceptorFactory;
    
    /** <默认构造函数> */
    public TaskInterceptorBeanPostProcessor() {
        super();
    }
    
    /** <默认构造函数> */
    public TaskInterceptorBeanPostProcessor(String module,
            TaskContextRegistry registry,
            TaskExecuteInterceptorFactory interceptorFactory) {
        super();
        AssertUtils.notEmpty(module, "module is empty.");
        AssertUtils.notNull(registry, "registry is null.");
        AssertUtils.notNull(interceptorFactory, "interceptorFactory is null.");
        
        this.module = module;
        this.registry = registry;
        this.interceptorFactory = interceptorFactory;
        
    }
    
    /**
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        AssertUtils.notEmpty(this.module, "module is empty.");
        AssertUtils.notNull(this.registry, "registry is null.");
        //注册切面
        
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean instanceof AopInfrastructureBean) {
            // Ignore AOP infrastructure such as scoped proxies.
            return bean;
        }
        
        //方法到任务的映射
        Map<Method, TaskDef> m2tMap = resolveMethod2TaskMap(bean, beanName);
        if (m2tMap.size() > 0) {
            ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
            if (!proxyFactory.isProxyTargetClass()) {
                evaluateProxyInterfaces(bean.getClass(), proxyFactory);
            }
            
            //构建新的拦截实例
            TaskExecuteInterceptor interceptor = this.interceptorFactory
                    .newInterceptor(m2tMap);
            Advisor advisor = this.advisorAdapterRegistry.wrap(interceptor);
            proxyFactory.addAdvisor(advisor);
            customizeProxyFactory(proxyFactory);
            
            // Use original ClassLoader if bean class not locally loaded in overriding class loader
            ClassLoader classLoader = getProxyClassLoader();
            if (classLoader instanceof SmartClassLoader
                    && classLoader != bean.getClass().getClassLoader()) {
                classLoader = ((SmartClassLoader) classLoader)
                        .getOriginalClassLoader();
            }
            
            //压入堆栈
            m2tMap.entrySet()
                    .stream()
                    .forEach(e -> {
                        //任务实例
                        TaskDef task = e.getValue();
                        //注册任务
                        this.registry.registerTask(task, signature);
                    });
            
            return proxyFactory.getProxy(classLoader);
        }
        
        // No proxy needed.
        return bean;
    }
    
    /**
     * 解析方法到任务定义的映射<br/>
     * <功能详细描述>
     * @param bean
     * @param beanName
     * @return [参数说明]
     * 
     * @return Map<Method,TaskDef> [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    protected Map<Method, TaskDef> resolveMethod2TaskMap(Object bean,
            String beanName) {
        final String finalModule = this.module;
        //方法到任务的映射
        Map<Method, TaskDef> resMap = new HashedMap<>();
        //解析方法到任务的映射
        Class<?> type = ClassUtils.getUserClass(bean);
        TaskClass taskClassAnno = AnnotationUtils.findAnnotation(type,
                TaskClass.class);
        if (taskClassAnno != null) {
            Method2TaskInfo m2t = new Method2TaskInfo(finalModule, bean,
                    taskClassAnno, beanName);
            resMap.put(m2t.method, m2t.taskDef);
        }
        //解析方法
        //如果非Executor的实现
        ReflectionUtils.doWithMethods(type, new MethodCallback() {
            @Override
            public void doWith(Method method)
                    throws IllegalArgumentException, IllegalAccessException {
                if (!Modifier.isPublic(method.getModifiers())) {
                    //如果非public方法则直接返回
                    return;
                }
                TaskMethod taskMethodAnnotation = AnnotationUtils
                        .getAnnotation(method, TaskMethod.class);
                if (taskMethodAnnotation != null) {
                    Method2TaskInfo m2t = new Method2TaskInfo(finalModule,
                            method, taskMethodAnnotation, beanName);
                    resMap.put(m2t.method, m2t.taskDef);
                }
            }
        });
        return resMap;
    }
    
    /**
     * 准备代理工厂<br/>
     * <功能详细描述>
     * @param bean
     * @param beanName
     * @return [参数说明]
     * 
     * @return ProxyFactory [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    protected ProxyFactory prepareProxyFactory(Object bean, String beanName) {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.copyFrom(this);
        proxyFactory.setTarget(bean);
        return proxyFactory;
    }
    
    /**
     * 自定义的代理工厂处理逻辑<br/>
     * <功能详细描述>
     * @param proxyFactory [参数说明]
     * 
     * @return void [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    protected void customizeProxyFactory(ProxyFactory proxyFactory) {
    }
    
    /**
     * 方法与任务定义的信息<br/>
     * <功能详细描述>
     * 
     * @author  PengQingyang
     * @version  [版本号, 2022年5月4日]
     * @see  [相关类/方法]
     * @since  [产品/模块版本]
     */
    protected static class Method2TaskInfo {
        
        /** 方法实例 */
        private Method method;
        
        /** 任务定义 */
        private final TaskDef taskDef;
        
        /** <默认构造函数> */
        public Method2TaskInfo(String module, Method method,
                TaskMethod taskMethodAnno, String beanName) {
            this.taskDef = new TaskDef();
            this.taskDef.setModule(module);
            
            this.method = method;
            this.taskDef.setBeanName(beanName);
            this.taskDef.setClassName(method.getDeclaringClass().getName());
            this.taskDef.setMethodName(method.getName());
            
            this.taskDef.setId(taskMethodAnno.id());
            this.taskDef.setName(taskMethodAnno.name());
            
            this.taskDef.setParentId(taskMethodAnno.parentId());
            this.taskDef.setPriority(taskMethodAnno.priority());
            this.taskDef.setRemark(taskMethodAnno.remark());
            this.taskDef.setParameters(taskMethodAnno.parameters());
            
            this.taskDef.setExecutable(taskMethodAnno.executable());
        }
        
        /** <默认构造函数> */
        public Method2TaskInfo(String module, Object bean,
                TaskClass taskClassAnno, String beanName) {
            //方法定义
            this.taskDef = new TaskDef();
            this.taskDef.setModule(module);
            
            Class<?> beanClass = ClassUtils.getUserClass(bean);
            String methodName = taskClassAnno.method();
            if (!StringUtils.hasText(methodName)) {
                throw new TaskContextInitException(
                        "TaskClass methodName is empty.");
            }
            
            //根据方法名匹配方法，当存在多个同名方法的情况下，才根据参数类型去匹配
            int methodCount = 0;
            for (Method m : beanClass.getMethods()) {
                if (methodName.equals(m.getName())) {
                    //只统计当前Bean的方法
                    methodCount = methodCount + 1;
                }
            }
            if (methodCount == 1) {
                this.method = BeanUtils
                        .findMethodWithMinimalParameters(beanClass, methodName);
            } else {
                //当同名方法大于的时候
                this.method = BeanUtils.findMethod(beanClass,
                        methodName,
                        taskClassAnno.parameterTypes());
            }
            if (this.method == null) {
                throw new TaskContextInitException(MessageUtils.format(
                        "taskClass method find error. taskClass:{}; methodName:{}; parameterTypes:{}",
                        new Object[] { beanClass.getName(), methodName,
                                taskClassAnno.parameterTypes() }));
            }
            
            this.taskDef.setBeanName(beanName);
            this.taskDef.setMethodName(methodName);
            this.taskDef.setClassName(method.getDeclaringClass().getName());
            
            this.taskDef.setId(invoke(bean,
                    beanClass,
                    taskClassAnno.getIdMethodName(),
                    taskClassAnno.id(),
                    String.class));
            this.taskDef.setName(invoke(bean,
                    beanClass,
                    taskClassAnno.getNameMethodName(),
                    taskClassAnno.name(),
                    String.class));
            
            this.taskDef.setParentId(invoke(bean,
                    beanClass,
                    taskClassAnno.getParentIdMethodName(),
                    taskClassAnno.parentId(),
                    String.class));
            this.taskDef.setRemark(invoke(bean,
                    beanClass,
                    taskClassAnno.getRemarkMethodName(),
                    taskClassAnno.remark(),
                    String.class));
            
            this.taskDef.setPriority(invoke(bean,
                    beanClass,
                    taskClassAnno.getPriorityMethodName(),
                    taskClassAnno.priority(),
                    Integer.class));
            this.taskDef.setExecutable(invoke(bean,
                    beanClass,
                    taskClassAnno.getExecutableMethodName(),
                    taskClassAnno.executable(),
                    Boolean.class));
            
            this.taskDef.setParameters(invokeParameters(bean,
                    beanClass,
                    taskClassAnno.getParametersMethodName(),
                    taskClassAnno.parameters()));
        }
        
        /**
         * 调用指定方法<br/>
         * <功能详细描述>
         * @param <T>
         * @param bean
         * @param beanClass
         * @param methodName
         * @param resultType
         * @return [参数说明]
         * 
         * @return T [返回类型说明]
         * @exception throws [异常类型] [异常说明]
         * @see [类、类#方法、类#成员]
         */
        @SuppressWarnings("unchecked")
        private <T> T invoke(Object bean, Class<?> beanClass, String methodName,
                T value, Class<T> resultType) {
            if (!StringUtils.hasText(methodName)) {
                return value;
            }
            
            Method m = BeanUtils.findMethod(beanClass, methodName);
            if (m == null) {
                return value;
            }
            //调用方法
            Object res = ReflectionUtils.invokeMethod(m, bean);
            if (resultType.isInstance(res)) {
                return (T) res;
            } else {
                return value;
            }
        }
        
        /**
         * 调用方法获取参数<br/>
         * <功能详细描述>
         * @param bean
         * @param beanClass
         * @param methodName
         * @param value
         * @return [参数说明]
         * 
         * @return String [返回类型说明]
         * @exception throws [异常类型] [异常说明]
         * @see [类、类#方法、类#成员]
         */
        @SuppressWarnings("unchecked")
        private String invokeParameters(Object bean, Class<?> beanClass,
                String methodName, String value) {
            if (!StringUtils.hasText(methodName)) {
                return value;
            }
            
            Method m = BeanUtils.findMethod(beanClass, methodName);
            if (m == null) {
                return value;
            }
            //调用方法
            Object res = ReflectionUtils.invokeMethod(m, bean);
            if (Map.class.isInstance(res)) {
                return (new JSONObject((Map<String, Object>) res))
                        .toJSONString();
            } else if (String.class.isInstance(res)) {
                return (String) res;
            } else {
                return value;
            }
        }
    }
    
    public static void main(String[] args)
            throws NoSuchMethodException, SecurityException {
        Class<?> beanClass = SimpleDailyTaskExecutor.class;
        TaskClass taskClassAnno = AnnotationUtils.findAnnotation(beanClass,
                TaskClass.class);
        String methodName = taskClassAnno.method();
        if (!StringUtils.hasText(methodName)) {
            throw new TaskContextInitException(
                    "TaskClass methodName is empty.");
        }
        
        Method method = null;
        //根据方法名匹配方法，当存在多个同名方法的情况下，才根据参数类型去匹配
        int methodCount = 0;
        for (Method m : beanClass.getMethods()) {
            //System.out.println(m);
            if (methodName.equals(m.getName())) {
                //只统计当前Bean的方法
                methodCount = methodCount + 1;
                System.out.println(beanClass.getMethod(m.getName(),
                        m.getParameterTypes()));
            }
        }
        if (methodCount == 1) {
            method = BeanUtils.findMethodWithMinimalParameters(beanClass,
                    methodName);
        } else {
            //当同名方法大于的时候
            method = BeanUtils.findMethod(beanClass,
                    methodName,
                    taskClassAnno.parameterTypes());
        }
        System.out.println(method);
    }
    
    /**
     * @return 返回 signature
     */
    public String getSignature() {
        return signature;
    }
    
    /**
     * @param 对signature进行赋值
     */
    public void setSignature(String signature) {
        this.signature = signature;
    }
    
    /**
     * @return 返回 module
     */
    public String getModule() {
        return module;
    }
    
    /**
     * @param 对module进行赋值
     */
    public void setModule(String module) {
        this.module = module;
    }
    
    /**
     * @return 返回 registry
     */
    public TaskContextRegistry getRegistry() {
        return registry;
    }
    
    /**
     * @param 对registry进行赋值
     */
    public void setRegistry(TaskContextRegistry registry) {
        this.registry = registry;
    }
    
    /**
     * @return 返回 interceptorFactory
     */
    public TaskExecuteInterceptorFactory getInterceptorFactory() {
        return interceptorFactory;
    }
    
    /**
     * @param 对interceptorFactory进行赋值
     */
    public void setInterceptorFactory(
            TaskExecuteInterceptorFactory interceptorFactory) {
        this.interceptorFactory = interceptorFactory;
    }
    
    //    /**
    //     * 方法缓存key
    //     *    
    //     * <功能详细描述>
    //     * 
    //     * @author  PengQingyang
    //     * @version  [版本号, 2022年5月11日]
    //     * @see  [相关类/方法]
    //     * @since  [产品/模块版本]
    //     */
    //    public static class MethodCacheKey {
    //        
    //        private Class<?> type;
    //        
    //        private String methodName;
    //        
    //        private Class<?> parameterTypes;
    //        
    //        /**
    //         * @return 返回 type
    //         */
    //        public Class<?> getType() {
    //            return type;
    //        }
    //        
    //        /**
    //         * @param 对type进行赋值
    //         */
    //        public void setType(Class<?> type) {
    //            this.type = type;
    //        }
    //        
    //        /**
    //         * @return 返回 methodName
    //         */
    //        public String getMethodName() {
    //            return methodName;
    //        }
    //        
    //        /**
    //         * @param 对methodName进行赋值
    //         */
    //        public void setMethodName(String methodName) {
    //            this.methodName = methodName;
    //        }
    //        
    //        /**
    //         * @return 返回 parameterTypes
    //         */
    //        public Class<?> getParameterTypes() {
    //            return parameterTypes;
    //        }
    //        
    //        /**
    //         * @param 对parameterTypes进行赋值
    //         */
    //        public void setParameterTypes(Class<?> parameterTypes) {
    //            this.parameterTypes = parameterTypes;
    //        }
    //    }
}
